package gbench.appdemo.myworld.market;



import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import gbench.appdemo.myworld.market.commodity.Commodity;
import gbench.appdemo.myworld.market.commodity.Price;
import gbench.appdemo.myworld.market.license.CommodityLicense;
import gbench.appdemo.myworld.market.license.ICommodityLicense;
import gbench.appdemo.myworld.market.trading.TradeBroker;
import gbench.appdemo.myworld.market.trading.TradeProxy;
import gbench.common.tree.LittleTree.Tuple2;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import static gbench.appdemo.myworld.market.commodity.Commodity.*;

/**
 * Economy 经济用于 表达需求
 * Market 用于实现求
 * @author gbench
 *
 */
public class SimpleMarket<UE extends Commodity> {
    
    /**
     * 
     * @param name
     * @param commodity
     */
    public SimpleMarket(String name,UE commodity){
        this.name = name;
        this.universalEquivalent = commodity;
        this.transactionStorage = new TransactionStorage();
    }
    
    /**
     * 为 participantId 颁布 商品 许可
     * @param participantId 交易者id
     * @return 生成一个商品交易许可
     */
    public CommodityLicense<Commodity,Commodity> getCommodityLicense(String participantId) {
        final var commodityLicense =  new CommodityLicense<Commodity,Commodity>(participantId) {
            @Override
            public Commodity getCommodity() {
                return COMMODITY(SimpleMarket.this.universalEquivalent.getName(),SimpleMarket.this.universalEquivalent.getUnitName());
            }
        };
        
        commodityLicense.setMarket(this);
        return commodityLicense;
     }
    
    /**
     * 为 participantId 颁布 商品 许可
     * @param participantId 角色名称
     * @param commodity 角色代理商品
     * @return
     */
    public CommodityLicense<Commodity,Commodity> getCommodityLicense(String participantId, final Commodity commodity) {
        
        final var commodityLicense =  new CommodityLicense<Commodity,Commodity>(participantId,commodity);
        commodityLicense.setMarket(this);
        commodityLicenses.add(commodityLicense);
        return commodityLicense;
     }
    
    /**
     * CommodityLicense
     * @param line 交易许可字符串
     * @return 商品交易许可
     */
    public CommodityLicense<Commodity,Commodity> parseCommodityLicense(String line) {
        String ss[] = line.split("[/,\\s\\\\]+");
        final String participantId = ss[0];
        final String commodity = ss[1];
        final String unitName = ss[2];
        return this.getCommodityLicense(participantId,COMMODITY(commodity,unitName));
    }
    
    /**
     * 批量生成 商品交易 许可
     * @param tuples {(participantId,commodity),(participantId1,commodity1),(participantId3,commodity3),...}
     * @return 交易许可集合。
     */
    public final Map<String,CommodityLicense<Commodity,Commodity>> getCommodityLicenses(List<Tuple2<String,Commodity>> tuples){
        final var map = new HashMap<String,CommodityLicense<Commodity,Commodity>>();
        tuples.forEach(e->{
            final var license = this.getCommodityLicense(e._1(), e._2());
            map.put(e._1(),license);
        });// stream
        return map;
    }

    /**
     * 批量生成 商品交易 许可
     * @param tuples {(participantId,commodity),(participantId1,commodity1),(participantId3,commodity3),...}
     * @return 交易许可集合。
     */
    @SafeVarargs
    public final Map<String,CommodityLicense<Commodity,Commodity>> getCommodityLicenses(Tuple2<String,Commodity> ... tuples){
       return this.getCommodityLicenses(Arrays.asList(tuples));
    }
    
    /**
     * 创建一个交易代理
     * @param tuples {(participantId,commodity),(participantId1,commodity1),(participantId3,commodity3),...}
     * @return 交易角色集合。返回的交易代理的默认角色为tuples 中的一个角色
     */
    @SafeVarargs
    public final TradeProxy<?> getTradeProxy(Tuple2<String,Commodity> ... tuples){
       return TradeProxy.of(tuples[0]._1(), this.getCommodityLicenses(tuples));
    }
    
    /**
     * 创建一个交易代理
     * @param tuples {(participantId,commodity),(participantId1,commodity1),(participantId3,commodity3),...}
     * @return 交易角色集合。返回的交易代理的默认角色为tuples 中的一个角色
     */
    public final TradeProxy<?> getTradeProxy(List<Tuple2<String,Commodity>>tuples){
       return TradeProxy.of(tuples.get(0)._1(), this.getCommodityLicenses(tuples));
    }
    
    /**
     * 创建一个交易代理
     * @param tuples 交易许可
     * @return 交易角色集合。返回的交易代理的默认角色为tuples 中的一个角色
     */
    @SafeVarargs
    public final TradeBroker getTradeBroker(Tuple2<String,Commodity> ... tuples){
       return new TradeBroker(getTradeProxy(tuples));
    }
    
    /**
     * 创建一个交易代理
     * @param tuples 交易许可
     * @return 交易角色集合。返回的交易代理的默认角色为tuples 中的一个角色
     */
    public final TradeBroker getTradeBroker(List<Tuple2<String,Commodity>> tuples){
       return new TradeBroker(getTradeProxy(tuples));
    }
    
    /**
     * 
     * 交易所事务
     * partA 支付 价格 获得  partB 的商品  <br> 
     * partB 接收价格 <br>
     * @author gbench
     *
     */
    @Data @AllArgsConstructor @NoArgsConstructor
    public class TradeTransaction<X extends Commodity,Y extends Commodity> {
        protected ICommodityLicense<X> partA;
        protected ICommodityLicense<Y> partB;
        protected LocalDateTime time;
        protected Price<X,Y> price;// 价格
        
        /**
         * drcr partA 与 partB 的借贷方向 DR：表示 正序 partA,partB,CR 表示 partB,partA
         */
        protected DRCR drcr;// 借贷方向
        
        /**
         *  na 单位的 a 与 nb 单位的 b 进行互换。
         * @param a
         * @param na
         * @param b
         * @param nb
         * @return
         */
        public Price<X,Y> setPrice(final X a, final Number na,Y b, final Number nb){
            final var price = SimpleMarket.price(a, na, b, nb);
            this.setPrice(price);
            return price;
        }
        
        /**
         * 
         * @param a A物品
         * @param na A的单位
         * @param b B物品
         * @param nb B单位
         * @return
         */
        public Price<X,Y> setPrice(final X a,final Number na, final Y b){
            final var price = SimpleMarket.price(a, na, b, 1);
            this.setPrice(price);
            return price;
        }
        
        /**
         * 
         */
        public TradeTransaction<X,Y> clone() {
            return new TradeTransaction<X,Y>(partA,partB,time,price,drcr);
        }
    }
    
    /**
     * 获取交易实例集合
     * @return 交易实例集合
     */
    public List<TradeTransaction<Commodity,Commodity>> getTransactions() {
        return transactionStorage.getTransactions();
    }

    /**
     * 商品交易
     * @return 创建一次商品交易
     */
    public void withTransaction(DRCR drcr,Consumer<TradeTransaction<Commodity,Commodity>>cs){
        final var trans = new TradeTransaction<Commodity,Commodity>(); // 交易事项
        trans.setDrcr(drcr);
        trans.setTime(LocalDateTime.now());
        cs.accept(trans);
        transactionStorage.addTransaction(trans);
    }
    
    /**
     * 商品交易
     * @return 创建一次商品交易
     */
    public void withDebitTransaction(Consumer<TradeTransaction<Commodity,Commodity>>cs){
        final var trans = new TradeTransaction<Commodity,Commodity>(); // 交易事项
        trans.setDrcr(DRCR.CR);
        trans.setTime(LocalDateTime.now());
        cs.accept(trans);
        transactionStorage.addTransaction(trans);
    }
    
    /**
     * 
     * @param <T> 借入科目 类型
     * @param <U> 待出科目 类型
     * 
     * @param drcr 借贷方向
     * @param ta 借入科目-名称
     * @param na 借入科目-数量
     * @param ub 贷出科目-名称
     * @param nb 贷出科目-数量
     * @param cs 回调函数
     */
    public <T extends Commodity,U extends Commodity> void withTransaction(
        final DRCR drcr,  // 借贷方向 
        final T ta, final Number na, final U ub, final Number nb, // 科目结构
        final Consumer<TradeTransaction<T,U>>cs) { // 回调函数
        
        final var trans = new TradeTransaction<T,U>(); // 交易事项
        final var price = price(ta,na,ub,nb);// 生成 ta 的价格
        
        trans.setDrcr(drcr);
        trans.setPrice(price);
        trans.setTime(LocalDateTime.now());
        
        cs.accept(trans);
        
        transactionStorage.addTransaction(trans);
    }

    /**
     * 
     * @param <T> 借方科目类型
     * @param <U> 贷方科目类型
     * 
     * @param ta 借入科目-名称
     * @param na 借入科目-数量
     * @param ub 贷出科目-名称
     * @param nb 贷出科目-数量
     * @param cs 回调函数
     */
    public <T extends Commodity,U extends Commodity> void credit(final T ta, final Number na, final U ub, final Number nb,
        final Consumer<TradeTransaction<T,U>>cs) {
        
       this.withTransaction(DRCR.CR, ta, na, ub, nb, cs);
    }
    
    /**
     * 
     * @param <T> 借方科目类型
     * @param <U> 贷方科目类型
     * 
     * @param ta 借入科目-名称
     * @param na 借入科目-数量
     * @param ub 贷出科目-名称
     * @param nb 贷出科目-数量
     * @param cs 回调函数
     */
    public <T extends Commodity,U extends Commodity> void debit(final T ta, final Number na, final U ub, final Number nb,
        final Consumer<TradeTransaction<T,U>>cs) {
        
       this.withTransaction(DRCR.DR, ta, na, ub, nb, cs);
    }
    
    public String getName() {
        return name;
    }
    
    public UE getUniversalEquivalent() {
        return universalEquivalent;
    }

    /**
     * 对A 进行计价
     * @param <T>
     * @param a
     * @param ratio
     * @return
     */
    public <T extends Commodity> Price<UE,T> price(T a,Number ratio) {
        return new Price<>(this.universalEquivalent,a,ratio.doubleValue());
    }
    
    /**
     * 获得 T的的UE价格
     * @param <T>
     * @param a
     * @return
     */
    public <T extends Commodity> Price<UE,T> getPrice(T a) {
        return new Price<>(this.universalEquivalent,a,1);
    }
    
    /**
     * a 相对于 b 的价格 或者 用B表示A的价格
     * 
     * @param a
     * @param amountA
     * @param b
     * @param amountB
     * @return
     */
    public static <T extends Commodity,U extends Commodity> Price<T,U> price(T a,Number amountA, U b,Number amountB) {
        return new Price<>(a,amountA,b,amountB);
    }

    /**
     * 
     * @param <T> 目标类型
     * @param <U> 一般等价物
     * @param a 目标
     * @param b 计价单位
     * @param ratio 比率
     * @return 价格对象
     */
    public static <T extends Commodity,U extends Commodity>  Price<T,U> price(T a, U b,Number ratio) {
        return new Price<>(a,b,ratio.doubleValue());
    }

    /**
     * 市场实例
     * @param line 市场对象解析
     * @return 市场实例
     */
    public static SimpleMarket<Commodity> of(String line){
        String ss[] = line.split("[/,\\s\\\\]+");
        return of(ss[0],ss[1],ss[2]);
    }
    
    /**
     * 创建市场实例
     * @param name 市场名称
     * @param commodity 商品对象
     * @param unitName 商品单位
     * @return 市场实例
     */
    public static SimpleMarket<Commodity> of(String name,String commodity,String unitName){
        return of(name,COMMODITY(commodity,unitName));
    }
    
    /**
     * 创建市场实例
     * @param name 市场名称
     * @param commodity 商品对象
     * @return 对象实例
     */
    public static SimpleMarket<Commodity> of(String name,Commodity commodity){
        return new SimpleMarket<Commodity>(name,commodity);
    }
    
    /**
     * 交易存储
     * @author gbench
     *
     */
    public class TransactionStorage{
        
        /**
         * 
         * @param trans
         */
        @SuppressWarnings("unchecked")
        public void addTransaction(final TradeTransaction<? extends Commodity,? extends Commodity> trans) {
            this.transactions.add((TradeTransaction<Commodity,Commodity>)trans);
        }
        
        /**
         * 
         * @return
         */
        public List<TradeTransaction<Commodity,Commodity>> getTransactions(){
            return this.transactions;
        }
        
        private final List<TradeTransaction<Commodity,Commodity>> transactions = new LinkedList<>();
    }// TransactionStorage
    
    /**
     * 
     * @return
     */
    public List<CommodityLicense<Commodity, Commodity>> getCommodityLicenses() {
        return commodityLicenses;
    }

    /**
     * 
     * @param commodityLicenses
     */
    public void setCommodityLicenses(List<CommodityLicense<Commodity, Commodity>> commodityLicenses) {
        this.commodityLicenses = commodityLicenses;
    }
    public List<CommodityLicense<Commodity, Commodity>> commodityLicenses = new LinkedList<>();
    public enum DRCR {DR,CR}; // 借贷方向
    protected final String name;// 市场名称
    protected final UE universalEquivalent;// 一般等价物
    protected final TransactionStorage transactionStorage;// 市场交易
}
